/***************************************************************************************************
 *
 * Project: HZFlashcards
 *
 * Class: HZFlashcardsDataModel
 *
 * File name: HZFlashcardsDataModel.java
 *
 * Aug 23, 2007
 *
 **************************************************************************************************/

package com.eastmountain.chinese.observable;

import java.awt.Font;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Observable;
import java.util.Properties;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import com.eastmountain.chinese.common.FlashCardLessonSet;
import com.eastmountain.chinese.db.exceptions.DBAccessException;
import com.eastmountain.chinese.db.flashcards.access.HZFindAccessor;
import com.eastmountain.chinese.db.flashcards.connection.CHPODSDBAccessData;
import com.eastmountain.chinese.db.flashcards.connection.DBAccessData;
import com.eastmountain.chinese.db.flashcards.connection.DBProperties;
import com.eastmountain.chinese.db.flashcards.connection.PCRSDBAccessData;
import com.eastmountain.chinese.db.flashcards.connection.RLCDBAccessData;
import com.eastmountain.chinese.db.flashcards.connection.TESTRLCDBAccessData;
import com.eastmountain.chinese.db.flashcards.records.ChineseCharacterProperties;
import com.eastmountain.chinese.db.flashcards.records.ChineseElementProperties;
import com.eastmountain.chinese.db.flashcards.records.ChineseWordProperties;
import com.eastmountain.chinese.db.flashcards.records.Lesson;
import com.eastmountain.chinese.db.flashcards.connection.MITDBAccessData;
import com.eastmountain.chinese.fullscreen.FramedSlideShow;
import com.eastmountain.chinese.printing.PrintingManager;
import com.eastmountain.util.PrintFile;
import com.eastmountain.util.PropertyHandler;
import com.eastmountain.util.logging.LoggerUtils;

/*******************************************************************************
 * 
 * CLASS HZFlashcardsDataModel
 * 
 ******************************************************************************/

public class HZFlashcardsDataModel extends Observable {

    private static String dbIndicator = "";

    private static HZFlashcardsDataModel hzFlashcardsDataModel;

    private static HZFindAccessor hzFindAccessor = null;
    
    private HZProperties hzProperties = new HZProperties();

    private PrintingManager printingManager;

    private boolean connected = false;

    private JFrame frame = null;

    private Vector<ChineseElementProperties> chineseElementProperties = null;

    private static FlashCardLessonSet flashCardLessonSet = null;
    private Vector<ChineseElementProperties> shuffledFlashCardLessons;

    private int[] lessonWorkingSet = null;
    private TreeMap<String, ChineseElementProperties> chineseElementReviewList = null;

    private ChineseElementProperties currentChineseElementProperties = null;

    private boolean multipleLessons = false;

    private int[] currentLessonNumbers = { 1 };

    public static final int VIEW_CHARACTERS = 0;
    public static final int VIEW_WORDS = 1;

    private Font activeHanZiFont = new Font( "Arial Unicode MS", Font.PLAIN, 40 );

    private int currentView = VIEW_CHARACTERS;

    private static String SOURCE_DB_PCRS    = "PCRS";
    private static String SOURCE_DB_CHPODS  = "CHPODS";
    private static String SOURCE_DB_MIT     = "MIT";
    private static String SOURCE_DB_RLC     = "RLC";
    private static String SOURCE_DB_TESTRLC = "TESTRLC";

    private String sourceDB = SOURCE_DB_PCRS;

    private static String PCRS_TREE_NAME   = "PracticalChinese Reader";
    private static String CHPODS_TREE_NAME = "ChinesePod";
    private static String MIT_TREE_NAME = "MIT";
    private static String RLC_TREE_NAME = "Rapid Literacy in Chinese";

    private String treeName = CHPODS_TREE_NAME;

    private static String[][] LESSON_LEVEL_NAMES_PCR = { { "I", "Book I" },
            { "II", "Book II" } };

    private static String[][] LESSON_LEVEL_NAMES_CHPOD = { { "A", "Newbie" },
            { "B", "Elementary" }, { "C", "Intermediate" },
            { "D", "Upper Intermediate" }, { "E", "Advanced" },
            { "F", "Media" } };
        
    private static String[][] LESSON_LEVEL_NAMES_MIT = { { "I", "Initials I" },
            { "R", "Rhymes" }, { "T", "Tones" } };
    
    private static String[][] LESSON_LEVEL_NAMES_RLC = { { "C", "Characters" } };
   
    private String[][] lessonLevelNames;

    private static String PCRS_DB_ACCESS_DATA = PCRSDBAccessData.class
            .getName();
    private static String CHPODS_DB_ACCESS_DATA = CHPODSDBAccessData.class
            .getName();
    private static String MIT_DB_ACCESS_DATA = MITDBAccessData.class
            .getName();
    private static String RLC_DB_ACCESS_DATA = RLCDBAccessData.class
    .getName();
    private static String TESTRLC_DB_ACCESS_DATA = TESTRLCDBAccessData.class
    .getName();

    private String dbAccessData = PCRS_DB_ACCESS_DATA;

    private String flashCardSource = "";

    private static String projectName = "gcflashcards";
    protected static Logger logger = LoggerUtils.setUpLogger( projectName );

    /***************************************************************************
     * 
     * HZFlashcardsDataModel
     * 
     **************************************************************************/

    private HZFlashcardsDataModel( String sourceDB ) {

        this.sourceDB = sourceDB;

        retrieveProperties();

        // TODO FIX HOW DB IS SET

        currentLessonNumbers = hzProperties.getLessonNumbers();

        connected = connectToDB( sourceDB );

        printingManager = new PrintingManager();

        chineseElementReviewList = new TreeMap<String, ChineseElementProperties>();
        
    }

    /***************************************************************************
     * 
     *  getInstance
     * 
     *  Note: since no dbIndicator came in as
     *        an arg to main, then one must be
     *        retrieved from the properties file.     
     * 
     **************************************************************************/

    public static HZFlashcardsDataModel getInstance() {

        if ( hzFlashcardsDataModel == null ) {

            
            
            hzFlashcardsDataModel = new HZFlashcardsDataModel( "" );

        }

        return hzFlashcardsDataModel;

    }

    /***************************************************************************
     * 
     * getInstance
     * 
     **************************************************************************/

    public static HZFlashcardsDataModel getInstance( String dbIndicator ) {

        if ( hzFlashcardsDataModel == null ) {

            hzFlashcardsDataModel = new HZFlashcardsDataModel( dbIndicator );

        }

        return hzFlashcardsDataModel;

    }

    public void initializeFlashCards() {

        try {

            flashCardLessonSet = createInitialFlashCardLessonSet();

        } catch ( DBAccessException e ) {

            logger.log( Level.SEVERE,
                    "***** Unable to create HZFlashcardsDataModel." );

            e.printStackTrace();

            System.exit( 12 );

        }
    }

    public void setMainFrame( JFrame frame ) {

        this.frame = frame;

    }

    /***************************************************************************
     * 
     * setChanged
     * 
     **************************************************************************/

    @Override
    public synchronized void setChanged() {

        super.setChanged();

    }

    /***************************************************************************
     * 
     * setView
     * 
     **************************************************************************/

    public void setView( int view ) {

        if ( currentView != view ) {

            currentView = view;

            try {

                flashCardLessonSet = getChineseElementsByLessonNumberList( currentLessonNumbers );

                if ( !flashCardLessonSet.isEmpty() ) {

                    hzFlashcardsDataModel.setChanged();
                    hzFlashcardsDataModel.notifyObservers( flashCardLessonSet );

                }

            } catch ( DBAccessException exception ) {

                // TODO Auto-generated catch block
                exception.printStackTrace();

            }

        }

    }

    /***************************************************************************
     * 
     * getActiveHanZiFont
     * 
     **************************************************************************/

    public Font getActiveHanZiFont() {

        return activeHanZiFont;

    }

    /***************************************************************************
     * 
     * setActiveHanZiFont
     * 
     **************************************************************************/

    public void setActiveHanZiFont( Font activeHanZiFont ) {

        this.activeHanZiFont = activeHanZiFont;

    }

    /***************************************************************************
     * 
     * getCurrentChineseElementProperties
     * 
     **************************************************************************/

    public ChineseElementProperties getCurrentChineseElementProperties() {

        return currentChineseElementProperties;

    }

    /***************************************************************************
     * 
     * setCurrentChineseElementProperties
     * 
     **************************************************************************/

    public void setCurrentChineseElementProperties(
            ChineseElementProperties currentChineseElementProperties ) {

        this.currentChineseElementProperties = currentChineseElementProperties;

    }

    /***************************************************************************
     * 
     * getMainFrame
     * 
     **************************************************************************/

    public JFrame getMainFrame() {

        return frame;

    }

    /***************************************************************************
     * 
     * getCurrentLessonNumbers
     * 
     **************************************************************************/

    public int[] getCurrentLessonNumbers() {

        return currentLessonNumbers;

    }

    /***************************************************************************
     * 
     * setCurrentLessonNumbers
     * 
     **************************************************************************/

    public void setCurrentLessonNumbers( int[] currentLessonNumbers ) {

        this.currentLessonNumbers = currentLessonNumbers;

    }

    /***************************************************************************
     * 
     * multipleLessons
     * 
     **************************************************************************/

    public boolean multipleLessons() {

        return multipleLessons;
    }

    /***************************************************************************
     * 
     * setMultipleLessons
     * 
     **************************************************************************/

    public void setMultipleLessons( boolean multipleLessons ) {

        this.multipleLessons = multipleLessons;

    }

    /***************************************************************************
     * 
     * findLessonForLessonNumber
     * 
     **************************************************************************/

    public Lesson getLessonForLessonNumber( int lessonNumber ) {

        Lesson lesson = new Lesson();

        try {

            lesson = hzFindAccessor.findLessonForLessonNumber( lessonNumber );                

        } catch ( DBAccessException e ) {

            // TODO Auto-generated catch block
            e.printStackTrace();

        }

        return lesson;

    }

    /***************************************************************************
     * 
     * getLessonsForLevel
     * 
     **************************************************************************/

    public Vector<Lesson> getLessonsForLevel( String level ) {

        Vector<Lesson> lessons = new Vector<Lesson>();

        try {
 
            lessons = hzFindAccessor.findLessonsForLevel( level );

        } catch ( DBAccessException e ) {

            // TODO Auto-generated catch block
            e.printStackTrace();

        }

        return lessons;

    }

    /***************************************************************************
     * 
     * getLowestAvailableLessonNumber
     * 
     **************************************************************************/

    public int getLowestAvailableLessonNumber() {

        int lessonNumber = 0;

        try {

            lessonNumber = hzFindAccessor.findMinimumLessonNumber();

        } catch ( DBAccessException e ) {

            // TODO Auto-generated catch block
            e.printStackTrace();

        }

        return lessonNumber;

    }

    /***************************************************************************
     * 
     * getLowestAvailableLesson
     * 
     **************************************************************************/

    public Lesson getLowestAvailableLesson() {

        int lessonNumber = 0;
        Lesson lesson = null;

        try {

            lessonNumber = hzFindAccessor.findMinimumLessonNumber();

            if ( lessonNumber > 0 ) {

                lesson = hzFindAccessor
                        .findLessonForLessonNumber( lessonNumber );

            }

        } catch ( DBAccessException e ) {

            // TODO Auto-generated catch block
            e.printStackTrace();

        }

        return lesson;

    }

    /***************************************************************************
     * 
     * getChineseElementsByLessonNumber
     * 
     **************************************************************************/

    public Vector<ChineseElementProperties> getChineseElementsByLessonNumber(
            int lessonNumber ) throws DBAccessException {

        chineseElementProperties = new Vector<ChineseElementProperties>();

        String errorMsg = "";

        try {

            switch (currentView) {

                case VIEW_CHARACTERS: // VIEW_CHARACTERS:

                    chineseElementProperties = getCharactersByLessonNumber( lessonNumber );

                    break;

                case VIEW_WORDS: // VIEW_WORDS:

                    chineseElementProperties = getWordsByLessonNumber( lessonNumber );

                    break;

                default:

                    errorMsg = "Invalid value for View encountered in "
                            + getClass().getSimpleName();

                    new Throwable( errorMsg ).printStackTrace();

                    break;

            }

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( null,
                    dBAccessException.getMessage(), "DB Error",
                    JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        }

        return chineseElementProperties;

    }

    /***************************************************************************
     * 
     * getChineseElementsByLessonNumberList
     * 
     **************************************************************************/

    public FlashCardLessonSet getChineseElementsByLessonNumberList(
            int[] lessonNumbers ) throws DBAccessException {

        Vector<Vector<ChineseElementProperties>> chineseElementPropertiesForLessonList = null;

        setCurrentLessonNumbers( lessonNumbers );

        chineseElementPropertiesForLessonList = new Vector<Vector<ChineseElementProperties>>();

        String errorMsg = "";

        try {

            switch (currentView) {

                case VIEW_CHARACTERS: // character flash cards

                    chineseElementPropertiesForLessonList = getCharactersByLessonNumberList( lessonNumbers );

                    break;

                case VIEW_WORDS:      // word flash cards

                    chineseElementPropertiesForLessonList = getWordsByLessonNumberList( lessonNumbers );

                    break;

                default:

                    errorMsg = "Invalid value for View encountered in "
                            + getClass().getSimpleName();

                    new Throwable( errorMsg ).printStackTrace();

                    break;

            }

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( null,
                    dBAccessException.getMessage(), "DB Error",
                    JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        }

        flashCardLessonSet = new FlashCardLessonSet(
                chineseElementPropertiesForLessonList );

        return flashCardLessonSet;

    }

    /***************************************************************************
     * 
     * getCharactersByLessonNumber
     * 
     **************************************************************************/

    public Vector<ChineseElementProperties> getCharactersByLessonNumber(
            int lessonNumber ) throws DBAccessException {

        try {

            chineseElementProperties = hzFindAccessor
                    .findCharacterPropertiesByLessonNumber( lessonNumber );

            setChanged();

            // setMultipleLessons( false );

            connected = true;

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( frame, dBAccessException
                    .getMessage(), "DB Error", JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        }

        return chineseElementProperties;

    }

    /***************************************************************************
     * 
     * getCharactersByLessonNumberList
     * 
     **************************************************************************/

    public Vector<Vector<ChineseElementProperties>> getCharactersByLessonNumberList(
            int[] lessonNumbers ) throws DBAccessException {

        Vector<Vector<ChineseElementProperties>> chineseElementPropertiesForLessonList = null;

        System.out
                .println( "****** HZFlashcardsDataModel.getCharactersByLessonNumberList() invoked" );

        int noLessons = lessonNumbers.length;

        Vector<ChineseElementProperties> chineseElementPropertiesForLesson = null;
        chineseElementPropertiesForLessonList = new Vector<Vector<ChineseElementProperties>>();

        try {

            for ( int i = 0; i < noLessons; i++ ) {

                chineseElementPropertiesForLesson = hzFindAccessor
                        .findCharacterPropertiesByLessonNumber( lessonNumbers[i] );

                System.out
                        .println( "****** chineseElementPropertiesForLesson size: "
                                + chineseElementPropertiesForLesson.size() );

                boolean success = chineseElementPropertiesForLessonList
                        .add( chineseElementPropertiesForLesson );

            }

            setChanged();

            if ( noLessons > 1 ) {

                setMultipleLessons( true );

            } else {

                setMultipleLessons( false );

            }

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( frame, dBAccessException
                    .getMessage(), "DB Error", JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        } catch ( java.lang.Exception e ) {

            // TODO add logging to aspect advice

            // logger.log( Level.SEVERE, "*** SELECT:
            // Exception:" + e.getClass().getName() + " MSG: "
            // + e.getMessage(), e );

            e.printStackTrace();

        }

        System.out
                .println( "****** chineseElementPropertiesForLessonList size: "
                        + chineseElementPropertiesForLessonList.size() );

        return chineseElementPropertiesForLessonList;

    }

    /***************************************************************************
     * 
     * getCharactersInWord
     * 
     **************************************************************************/

    public Vector<ChineseCharacterProperties> getCharactersInWord( String word ) {

        Vector<ChineseCharacterProperties> chineseCharactersInWord = null;

        try {

            chineseCharactersInWord = hzFindAccessor
                    .findCharacterPropertiesForWord( word );

            setChanged();

            connected = true;

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( null,
                    dBAccessException.getMessage(), "DB Error",
                    JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        }

        return chineseCharactersInWord;

    }

    /***************************************************************************
     * 
     * getWordsByLessonNumber
     * 
     **************************************************************************/

    public Vector<ChineseElementProperties> getWordsByLessonNumber(
            int lessonNumber ) {

        try {

            chineseElementProperties = hzFindAccessor
                    .findWordPropertiesByLessonNumber( lessonNumber );

            setChanged();

            // setMultipleLessons( false );

            connected = true;

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( frame, dBAccessException
                    .getMessage(), "DB Error", JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        }

        return chineseElementProperties;

    }

    /***************************************************************************
     * 
     * getWordsByLessonNumberList
     * 
     **************************************************************************/

    public Vector<Vector<ChineseElementProperties>> getWordsByLessonNumberList(
            int[] lessonNumbers ) throws DBAccessException {

        Vector<Vector<ChineseElementProperties>> chineseElementPropertiesForLessonList = null;

        System.out.println( "****** getWordsByLessonNumberList() invoked" );

        int noLessons = lessonNumbers.length;

        Vector<ChineseElementProperties> chineseElementPropertiesForLesson = null;
        chineseElementPropertiesForLessonList = new Vector<Vector<ChineseElementProperties>>();

        try {

            for ( int i = 0; i < noLessons; i++ ) {

                chineseElementPropertiesForLesson = hzFindAccessor
                        .findWordPropertiesByLessonNumber( lessonNumbers[i] );

                System.out
                        .println( "****** chineseElementPropertiesForLesson size: "
                                + chineseElementPropertiesForLesson.size() );

                boolean success = chineseElementPropertiesForLessonList
                        .add( chineseElementPropertiesForLesson );

            }

            setChanged();

            if ( noLessons > 1 ) {

                setMultipleLessons( true );

            } else {

                setMultipleLessons( false );

            }

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( frame, dBAccessException
                    .getMessage(), "DB Error", JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        } catch ( java.lang.Exception e ) {

            // TODO add logging to aspect advice

            // logger.log( Level.SEVERE, "*** SELECT:
            // Exception:" + e.getClass().getName() + " MSG: "
            // + e.getMessage(), e );

            e.printStackTrace();

        }

        System.out
                .println( "****** chineseElementPropertiesForLessonList size: "
                        + chineseElementPropertiesForLessonList.size() );

        return chineseElementPropertiesForLessonList;

    }

    /***************************************************************************
     * 
     * getWordsContainingCharacter
     * 
     **************************************************************************/

    public Vector<ChineseWordProperties> getWordsContainingCharacter(
            String hanZi ) {

        Vector<ChineseWordProperties> chineseWordsContainingCharacter = null;

        try {

            
            chineseWordsContainingCharacter = hzFindAccessor
                    .findWordsContainingCharacter( hanZi );

            setChanged();

            connected = true;

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( null,
                    dBAccessException.getMessage(), "DB Error",
                    JOptionPane.ERROR_MESSAGE );

            // dBAccessException.printStackTrace();

        }

        return chineseWordsContainingCharacter;

    }

    /***************************************************************************
     * 
     * getLessonWorkingSet
     * 
     **************************************************************************/

    public TreeSet getLessonWorkingSet() {

        TreeSet lessonWorkingSetTree = new TreeSet( Arrays
                .asList( lessonWorkingSet ) );

        return lessonWorkingSetTree;

    }

    /***************************************************************************
     * 
     * getReviewList // TODO is this of use?
     * 
     **************************************************************************/

    public TreeMap getReviewList() {

        return chineseElementReviewList;

    }

    /***************************************************************************
     * 
     * addElementToReviewList
     * 
     **************************************************************************/

    public void addElementToReviewList() {

        chineseElementReviewList.put( currentChineseElementProperties.getChineseElement(), 
                                      currentChineseElementProperties );

    }

    /***************************************************************************
     * 
     * createInitialFlashCardLessonSet
     * 
     **************************************************************************/

    public FlashCardLessonSet createInitialFlashCardLessonSet()
            throws DBAccessException {
        
        FlashCardLessonSet flashCardLessonSet = null;

        System.out.println( "*****  Attempting to create VOCABULARY "
                + "FlashCards" );

        flashCardLessonSet = createInitialVocabularyFlashCardSet();

        return( flashCardLessonSet );

    }

    /***************************************************************************
     * 
     * createInitialVocabularyFlashCardSet
     * 
     **************************************************************************/

    private FlashCardLessonSet createInitialVocabularyFlashCardSet()
            throws DBAccessException {
        
        // TODO Make sure this works when there
        // is no property file or it is invalid.

        int[] lessonNumbers = null;
        Vector<ChineseElementProperties> lessonFlashCards = null;

        // TODO working set?

        if ( ( lessonNumbers = hzProperties.getLessonNumbers() ) != null ) {

            // TODO Deal with lesson list in properties,
            // but one or more lessons not in DB?

            try {

                flashCardLessonSet = getChineseElementsByLessonNumberList( lessonNumbers );

                lessonWorkingSet = lessonNumbers;
                currentLessonNumbers = lessonNumbers;

                System.out
                        .println( "****** createInitialFlashCardLessonSet() got flashCardLessonSet" );

            } catch ( DBAccessException e ) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        } else {

            int initialLessonNumber = getLowestAvailableLessonNumber();

            if ( !( initialLessonNumber > 0 ) ) {

                logger.log( Level.INFO, "***** HZFlashcardsDataModel - "
                        + "Initial Lesson Number not 1" );

            }

            System.out
                    .println( "****** createInitialFlashCardLessonSet() trying plan B." );

            flashCardLessonSet = new FlashCardLessonSet();

            lessonFlashCards = getChineseElementsByLessonNumber( initialLessonNumber );

            flashCardLessonSet.add( lessonFlashCards );

            lessonNumbers = new int[1];
            lessonNumbers[0] = initialLessonNumber;

            lessonWorkingSet = lessonNumbers;
            currentLessonNumbers = lessonNumbers;

        }

        // TODO what if we haven't found any lessons?

        return flashCardLessonSet;
        
    }

    

    /***************************************************************************
     * 
     * createInitialSoundsFlashCardSet
     * 
     **************************************************************************/
/*
    private FlashCardLessonSet createInitialSoundsFlashCardSet()
            throws DBAccessException {

        Vector<TonesRecord> tonesRecords;
        
        Vector<ChineseElementProperties> chineseWordPropertiesWithSound
            = new Vector<ChineseElementProperties>();
        Vector<Vector<ChineseElementProperties>> soundSetList
            = new Vector<Vector<ChineseElementProperties>>();
        
        FlashCardLessonSet flashCardLessonSet = null;
        
        //try {
 
            chineseWordPropertiesWithSound
                = soundsDBAccessor.getTonesRecordByLevel( TonesRecord.RecordTypes.TONE );

            soundSetList.add(chineseWordPropertiesWithSound );
        
            flashCardLessonSet = new FlashCardLessonSet( soundSetList );
            
            setChanged();

            connected = true;

        } catch ( DBAccessException dBAccessException ) {

            connected = false;

            JOptionPane.showMessageDialog( null,
                    dBAccessException.getMessage(), "DB Error",
                    JOptionPane.ERROR_MESSAGE );

            dBAccessException.printStackTrace();

        }

        return( flashCardLessonSet );
        
    }
*/
    /***************************************************************************
     * 
     * shuffleFlashcards
     * 
     **************************************************************************/

    public Vector<ChineseElementProperties> shuffleFlashcards() {

        System.out
                .println( "\n****** HZFlashcardsDataModel.shuffleFlashcards() invoked" );

        shuffledFlashCardLessons = flashCardLessonSet.shuffle();

        return shuffledFlashCardLessons;

    }

    /***************************************************************************
     * 
     * printReviewList
     * 
     **************************************************************************/

    public void printReviewList() {

        System.out
                .println( "\n****** HZFlashcardsDataModel.printReviewList() invoked" );

        printingManager.printReviewList( chineseElementReviewList );

    }

    /***************************************************************************
     * 
     * printCharacterWordXRFList
     * 
     **************************************************************************/

    public void printCharacterWordXRFList() {

        String hanZi;

        System.out
                .println( "\n****** HZFlashcardsDataModel.printCharacterWordXRFList() invoked" );

        hanZi = currentChineseElementProperties.getChineseElement();

        Vector<ChineseWordProperties> chineseWordsForCharacter = getWordsContainingCharacter( hanZi );

        printingManager.printCharacterWordXRFList( hanZi,
                chineseWordsForCharacter );

    }

    /***************************************************************************
     * 
     * printLWordCharacterBreakdownList
     * 
     **************************************************************************/

    public void printLWordCharacterBreakdownList() {

        String word;

        System.out
                .println( "\n****** HZFlashcardsDataModel.printLWordCharacterBreakdownList() invoked" );

        word = currentChineseElementProperties.getChineseElement();

        Vector<ChineseCharacterProperties> chineseCharactersForWord = getCharactersInWord( word );

        printingManager.printWordCharacterBreakdownListPane( word,
                chineseCharactersForWord );

    }

    /***************************************************************************
     * 
     * startSlideShow
     * 
     **************************************************************************/

    public void startSlideShow() {

        System.out
                .println( "\n****** HZFlashcardsDataModel.startSlideShow() invoked" );

        Font font = getActiveHanZiFont();
        Font biggerFont = font.deriveFont( 240 );

        PrintFile.println();

        PrintFile.println( "*** Font chosen in HZFlashcardsDataModel:\n", "" );
        PrintFile.println( "font Name", biggerFont.getName() );
        PrintFile.println( "font Style", biggerFont.getStyle() );
        PrintFile.println( "font Size", biggerFont.getSize() );

        FramedSlideShow framedSlideShow = new FramedSlideShow( biggerFont );
        framedSlideShow.begin( flashCardLessonSet );

    }

    /***************************************************************************
     * 
     * openLessonWorkingSet
     * 
     **************************************************************************/

    public FlashCardLessonSet openLessonWorkingSet( File file ) {

        int[] intWorkingSet;

        // TODO Make sure that we're getting the correct type

        ArrayList<Integer> workingSet = (ArrayList<Integer>) readObjectXML( file );

        intWorkingSet = new int[workingSet.size()];

        for ( int i = 0; i < workingSet.size(); i++ ) {

            intWorkingSet[i] = ( workingSet.get( i ) ).intValue();

        }

        lessonWorkingSet = intWorkingSet;

        try {

            FlashCardLessonSet flashCardLessonSet = getChineseElementsByLessonNumberList( lessonWorkingSet );

        } catch ( DBAccessException e ) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        setCurrentLessonNumbers( lessonWorkingSet );

        return flashCardLessonSet;

    }

    /***************************************************************************
     * 
     * openReviewList
     * 
     **************************************************************************/

    public FlashCardLessonSet openReviewList( File file ) {

        // TODO why not use the constructor or create one
        
        FlashCardLessonSet flashCardLessonSet = new FlashCardLessonSet();

        chineseElementReviewList = (TreeMap<String, ChineseElementProperties>) readObjectXML( file );

        flashCardLessonSet.add( chineseElementReviewList );

        return flashCardLessonSet;

    }

    /***************************************************************************
     * 
     * saveLessonWorkingSet
     * 
     **************************************************************************/

    public void saveLessonWorkingSet( File file ) {

        lessonWorkingSet = currentLessonNumbers;

        ArrayList<Integer> arrayList = new ArrayList<Integer>();

        for ( int lesson : lessonWorkingSet ) {

            arrayList.add( Integer.valueOf( lesson ) );

        }

        writeObjectXML( arrayList, file );

    }

    /***************************************************************************
     * 
     * saveReviewList
     * 
     **************************************************************************/

    public void saveReviewList( File file ) {

        writeObjectXML( chineseElementReviewList, file );

    }

    /***************************************************************************
     * 
     * readObjectXML
     * 
     **************************************************************************/

    public Object readObjectXML( File file ) {

        System.out.println( "*****  readObjectXML  ******" );

        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        XMLDecoder decoder = null;

        Object inputObject = null;

        try {
            fileInputStream = new FileInputStream( file.getName() );
            bufferedInputStream = new BufferedInputStream( fileInputStream );
            decoder = new XMLDecoder( bufferedInputStream );

            inputObject = decoder.readObject();

            decoder.close();

        } catch ( FileNotFoundException e1 ) {

            // TODO Auto-generated catch block

            e1.printStackTrace();

        }

        return inputObject;

    }

    /***************************************************************************
     * 
     * writeObjectXML
     * 
     **************************************************************************/

    public boolean writeObjectXML( Object object, File file ) {

        System.out.println( "*****  writeObjectXML  ******" );

        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        XMLEncoder encoder = null;

        try {

            fileOutputStream = new FileOutputStream( file.getName() );
            bufferedOutputStream = new BufferedOutputStream( fileOutputStream );
            encoder = new XMLEncoder( bufferedOutputStream );

            encoder.writeObject( object );

            encoder.close();

        } catch ( FileNotFoundException e1 ) {

            // TODO Auto-generated catch block

            e1.printStackTrace();

        }

        return true;

    }

    /***************************************************************************
     * 
     * retrieveProperties
     * 
     **************************************************************************/

    public Properties retrieveProperties() {

        Properties applicationProps;
        String     characterLessonNumbers;
        int[]      lessonNumbers = null;
        int[]      defaultLessonNumbers = { 1 };
        String     fontAttributes;

        System.out.println( "\nLoading application properties" );
        logger.log( Level.INFO, "Loading application properties" );
        
        hzProperties = new HZProperties();

        applicationProps = PropertyHandler.load();
        
        if ( sourceDB.equals( "" ) ) {
            
            sourceDB = applicationProps.getProperty( "source-db" );            
            
        }

        if ( ( sourceDB != null ) ) {

            hzProperties.setsourceDB( sourceDB );

            if ( sourceDB.equals( SOURCE_DB_PCRS ) ) {

                setSourceDB( SOURCE_DB_PCRS );
                setTreeName( PCRS_TREE_NAME );
                setLessonLevelNames( LESSON_LEVEL_NAMES_PCR );        

            } else if ( sourceDB.equals( SOURCE_DB_CHPODS ) ) {

                setSourceDB( SOURCE_DB_CHPODS );
                setTreeName( CHPODS_TREE_NAME );
                setLessonLevelNames( LESSON_LEVEL_NAMES_CHPOD );        

            } else if ( sourceDB.equals( SOURCE_DB_MIT ) ) {

                setSourceDB( SOURCE_DB_MIT );
                setTreeName( MIT_TREE_NAME );
                setLessonLevelNames( LESSON_LEVEL_NAMES_MIT );
                
            } else if ( sourceDB.equals( SOURCE_DB_RLC ) ) {

                setSourceDB( SOURCE_DB_RLC );
                setTreeName( RLC_TREE_NAME );
                setLessonLevelNames( LESSON_LEVEL_NAMES_RLC );
                
            } else if ( sourceDB.equals( SOURCE_DB_TESTRLC ) ) {

                setSourceDB( SOURCE_DB_TESTRLC );
                setTreeName( RLC_TREE_NAME );
                setLessonLevelNames( LESSON_LEVEL_NAMES_RLC );        

            } else {
                
                //  add warning

                setSourceDB( SOURCE_DB_PCRS );
                setTreeName( PCRS_TREE_NAME );
                setLessonLevelNames( LESSON_LEVEL_NAMES_PCR );        

            }

        } else {
            
            // SourceDB must be set from input arg

            hzProperties.setsourceDB( null );

        }

        System.out.println( "\nsourceDB = " + sourceDB );
        logger.log( Level.INFO, "sourceDB = " + sourceDB );

        characterLessonNumbers = applicationProps.getProperty( "lessons" );

        if ( characterLessonNumbers != null ) {

            lessonNumbers = stringArrayToIntArray( characterLessonNumbers );

        } else {

            lessonNumbers = null;

        }

        hzProperties = new HZProperties();

        if ( lessonNumbers != null ) {

            hzProperties.setLessonNumbers( lessonNumbers );

            //for ( int i = 0; i < lessonNumbers.length; i++ ) {

              //  System.out.println( "Lessons: value = " + lessonNumbers[i] );

            //}

        } else {
            
            lessonNumbers = defaultLessonNumbers;

            hzProperties.setLessonNumbers( lessonNumbers );

        }

        System.out.println( "lessons = " + lessonNumbers );
        logger.log( Level.INFO, "lessons = " + lessonNumbers.toString() );
        
        fontAttributes = applicationProps.getProperty( "font-attributes" );

        if ( fontAttributes != null ) {

        } else {
            
            fontAttributes = "";

        }

        System.out.println( "fontAttributes = " + fontAttributes );
        logger.log( Level.INFO, "fontAttributes = " + fontAttributes );
        
        return applicationProps;

    }

    /***************************************************************************
     * 
     * saveProperties
     * 
     **************************************************************************/

    public void saveProperties() {

        String currentLessonNumbers;
        Font activeHanZiFont;
        String fontAttributes;

        Properties applicationProps = new Properties();

        applicationProps.setProperty( "source-db", sourceDB );

        currentLessonNumbers = Arrays.toString( getCurrentLessonNumbers() );

        applicationProps.setProperty( "lessons", currentLessonNumbers );
       
        activeHanZiFont = getActiveHanZiFont();
        
        fontAttributes
            = activeHanZiFont.getFamily() + ","
            + Integer.toString( activeHanZiFont.getStyle() ) + ","
            + Integer.toString( activeHanZiFont.getSize() );            

        applicationProps.setProperty( "font-attributes", fontAttributes );
        
        PropertyHandler.store( applicationProps );

    }

    /***************************************************************************
     * 
     * stringArrayToIntArray // There must be a better // way?
     * 
     **************************************************************************/

    private static int[] stringArrayToIntArray( String array ) {

        int[] arrayValues = null;
        int arraySize = 0;

        Vector<String> literalValues = new Vector<String>();

        Scanner scanner;

        scanner = new Scanner( array );
        scanner.useDelimiter( "[^0-9]+" );

        while ( scanner.hasNext() ) {

            if ( scanner.hasNextInt() ) {

                String value = scanner.next();
                System.out.println( "digits found = " + value );
                literalValues.add( value );

            } else {
                scanner.next();
            }

        }

        if ( !literalValues.isEmpty() ) {

            arraySize = literalValues.size();

            arrayValues = new int[arraySize];

            for ( int i = 0; i < arraySize; i++ ) {

                arrayValues[i] = Integer.parseInt( literalValues.get( i ) );

            }

        }

        return arrayValues;

    }

    /***************************************************************************
     * 
     * connectToDB
     * 
     **************************************************************************/

    private boolean connectToDB( String sourceDB ) {

        // TODO Verify that the DB is not already open.
        
        DBProperties dbProperties;
        DBAccessData dbAccessData = null;

        try {

            dbProperties = new DBProperties( sourceDB );
            dbAccessData = dbProperties.getDbAccessData();
            
            hzFindAccessor = new HZFindAccessor( dbAccessData );
            flashCardSource = dbAccessData.getFLASHCARD_SOURCE_TEXT();

        } catch ( DBAccessException dBAccessException ) {

            // Assume this will be caught in HZAspects.aj

            dBAccessException.printStackTrace();

            connected = false;

            JOptionPane.showMessageDialog( frame, dBAccessException
                    .getMessage(),
                    "DB Error in HZFlashcardsDataModel.connectToDB()",
                    JOptionPane.ERROR_MESSAGE );

            System.exit( 12 );

        } catch ( Exception exception ) {

            exception.printStackTrace();

            connected = false;

            JOptionPane.showMessageDialog( frame, exception.getMessage(),
                    "DB Error", JOptionPane.ERROR_MESSAGE );

            System.exit( 12 );

        }

        return connected;

    }

    /***************************************************************************
     * 
     * shutdown
     * 
     **************************************************************************/

    public void shutdown() throws DBAccessException {

        System.out
                .println( "***** HZFlashcardsDataModel.shutdown() invoked  ****" );

        saveProperties();

        hzFindAccessor.shutdownDB();

    }

    public String getTreeName() {
        return treeName;
    }

    public void setTreeName( String treeName ) {
        this.treeName = treeName;
    }

    public String[][] getLessonLevelNames() {

        return lessonLevelNames;

    }
    
    public void setLessonLevelNames( String[][] lessonLevelNames ) {
        
        this.lessonLevelNames = lessonLevelNames;    
        
    }

    public String getSourceDB() {
        return sourceDB;
    }

    public void setSourceDB( String sourceDB ) {
        this.sourceDB = sourceDB;
    }

    public String getFlashCardSource() {
        return flashCardSource;
    }

}

/*******************************************************************************
 * 
 * CLASS HZProperties
 * 
 ******************************************************************************/

class HZProperties {

    private String sourceDB;
    private int[] lessonNumbers;

    public String getsourceDB() {
        return sourceDB;
    }

    public void setsourceDB( String sourceDB ) {
        this.sourceDB = sourceDB;
    }

    public int[] getLessonNumbers() {

        return lessonNumbers;

    }

    public void setLessonNumbers( int[] lessonNumbers ) {

        this.lessonNumbers = lessonNumbers;

    }

}

class LessonWorkingSet<T> extends ArrayList implements Serializable {

    public LessonWorkingSet() {

    }

}

class ReviewList<T> extends ArrayList {

}